﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Configuration;
using System.Collections.Specialized;
using System.Threading;
using Vbyte.DataSource.Cache;
using Vbyte.Configuration;
using Vbyte.Extension;

#if UnitTest
using NUnit.Framework;
#endif

namespace Vbyte.DataSource.Data
{
    /// <summary>
    /// 数据获取代理
    /// </summary>
    /// <typeparam name="T">有无参数的公开构造函数，实现了属性标识识别和缓存时间支持的接口类。</typeparam>
    public class DataProxy<T>
          where T : class, IObjectIdentification<PropertyBindIdentity>, ICacheTimeSupport, new()
    {
        /// <summary>
        /// 获取或设置改数据代理的缓存策略
        /// </summary>
        public ICachePolicy<T, PropertyBindIdentity> CachePolicy { get; set; }

        private void InitializePolicy(Type policyType)
        {
            Type tChk = policyType.GetInterface(typeof(ICachePolicy<,>).FullName, false);
            if (tChk == null)
            {
                throw new ConfigurationErrorsException("实现缓存策略的类必须实现接口：" + typeof(ICachePolicy<T, PropertyBindIdentity>).FullName + "！");
            }
            else
            {
                CachePolicy = (ICachePolicy<T, PropertyBindIdentity>)Activator.CreateInstance(policyType);
            }
        }

        private void InitializePolicy(string cachePolicyFullName)
        {
            Type policyType = Type.GetType(cachePolicyFullName, false);
            if (policyType == null)
            {
                throw new ConfigurationErrorsException("缓存策略配置错误，未能找到实现缓存策略的类！");
            }
            else
            {
                //一个泛型参数
                if (policyType.IsGenericType && CacheConfig.ConfigInstance.ImpTypeFullName.EndsWith("`1"))
                {
                    policyType = policyType.MakeGenericType(typeof(T));
                }
                else
                {
                    throw new ConfigurationErrorsException("实现缓存策略的类必须为实现类型的重整名称，形如：\"BestTone.DataSource.Cache.OrmCachedPolicy`1\"，一个泛型类型参数！");
                }
                InitializePolicy(policyType);
            }
        }

        /// <summary>
        /// 使用缓存获取指定实例类型的实例
        /// </summary>
        /// <param name="identity">实例绑定标志</param>
        /// <exception cref="System.Configuration.ConfigurationErrorsException">实例远程对象获取配置异常</exception>
        /// <exception cref="System.NotSupportedException">远程数据格式不被支持</exception>
        /// <exception cref="BestTone.DataSource.ApiException">远程API数据获取错误</exception>
        /// <returns>如果本地缓存已实现，则从缓存读取，否则从远程读取实例绑定。</returns>
        public T GetEntry(PropertyBindIdentity identity)
        {
            return GetEntry(identity, true);
        }

        /// <summary>
        /// 获取指定实例类型的实例
        /// </summary>
        /// <param name="identity">实例绑定标志</param>
        /// <param name="useCache">是否使用本地缓存</param>
        /// <exception cref="System.Configuration.ConfigurationErrorsException">实例远程对象获取配置异常</exception>
        /// <exception cref="System.NotSupportedException">远程数据格式不被支持</exception>
        /// <exception cref="BestTone.DataSource.ApiException">远程API数据获取错误</exception>
        /// <returns>如果本地缓存已实现，则从缓存读取，否则从远程读取实例绑定。</returns>
        public T GetEntry(PropertyBindIdentity identity, bool useCache)
        {
            bool blnThrow = (!string.IsNullOrEmpty(ConfigurationManager.AppSettings["DataSource.ThrowException"])
                && Convert.ToBoolean(ConfigurationManager.AppSettings["DataSource.ThrowException"]));

            try
            {
                Type instanceType = typeof(T);
                string instanceTypeName = instanceType.FullName;

                #region 根据缓存策略规则从缓存加载 ORM或文件系统
                if (CachePolicy == null)
                {
                    object[] cachePAttr = instanceType.GetCustomAttributes(typeof(CachePolicyAttribute), true);
                    if (cachePAttr != null && cachePAttr.Length > 0)
                    {
                        InitializePolicy( ((CachePolicyAttribute)cachePAttr[0]).CachePolicyType );
                    }
                    else
                    {
                        InitializePolicy(CacheConfig.ConfigInstance.ImpTypeFullName);
                    }
                }
                #endregion

                T result = new T();
                
                if (instanceType.HasAttribute(typeof(ToDoAttribute), true))
                {
                    #region 未实现则直接从远程加载
                    result = CachePolicy.Load(identity, false);
                    #endregion
                }
                else
                {
                    CachePolicy.Mode = CacheConfig.ConfigInstance.Mode;
                    CachePolicy.CacheMinutes = CacheConfig.ConfigInstance.CacheMinutes;

                    #region 特定类型的缓存配置
                    if (CacheConfig.ConfigInstance.SpecialCaches != null && CacheConfig.ConfigInstance.SpecialCaches.Length > 0)
                    {
                        foreach (SpecialCache sCache in CacheConfig.ConfigInstance.SpecialCaches)
                        {
                            if (sCache.CacheType.Equals(instanceTypeName))
                            {
                                CachePolicy.CacheMinutes = sCache.CacheMinutes;
                                CachePolicy.Mode = sCache.Mode;
                                break;
                            }
                        }
                    }
                    #endregion

                    result = CachePolicy.Load(identity, useCache);

                    #region 比较缓存是否需要更新
                    bool needRefreshCache = false;
                    if (CachePolicy.Mode == CacheCompareMode.LocalCacheTime)
                    {
                        if (result.GetCacheTimeUTC().AddMinutes(CachePolicy.CacheMinutes) < DateTime.Now.ToUniversalTime())
                        {
                            needRefreshCache = true;
                        }
                    }
                    else
                    {
                        if (result.GetUpdateTimeUTC().AddMinutes(CachePolicy.CacheMinutes) < DateTime.Now.ToUniversalTime())
                        {
                            needRefreshCache = true;
                        }
                    }

                    if (needRefreshCache)
                    {
                        new Thread(r => { CachePolicy.RefreshCache(identity); }).Start();
                    }
                    #endregion

                }
                identity.BindInstance(instanceType, result); 
                return result;
            }
            catch (Exception exp)
            {
                //if (exp is ApiException)
                //{
                //    DataExchangeHelper.ThrowException(typeof(T), exp, "Error-API-" + DateTime.Now.ToString("yyyy-MM-dd"), identity);
                //}
                //else
                //{
                //    DataExchangeHelper.ThrowException(typeof(T), exp, "Error-Local-" + DateTime.Now.ToString("yyyy-MM-dd"), identity);
                //}
            }
            return new T();
        }
        
        /// <summary>
        /// 删除指定标志的数据
        /// </summary>
        /// <param name="identity">实例绑定标志</param>
        /// <returns>操作成功则为字符0，其他为操作异常。</returns>
        public void RemoveEntry(PropertyBindIdentity identity)
        {
            try
            {
                CachePolicy.RemoveCache(identity);
            }
            catch (Exception exp)
            {
                DataExchangeHelper.ThrowException(typeof(T), exp, "Error-Local-" + DateTime.Now.ToString("yyyy-MM-dd"), identity);
            }
        }
        
        /// <summary>
        /// 使用缓存加载指定实例类型的实例
        /// </summary>
        /// <param name="identity">实例绑定标志</param>
        /// <exception cref="System.Configuration.ConfigurationErrorsException">实例远程对象获取配置异常</exception>
        /// <exception cref="System.NotSupportedException">远程数据格式不被支持</exception>
        /// <exception cref="BestTone.DataSource.ApiException">远程API数据获取错误</exception>
        /// <returns>如果本地缓存已实现，则从缓存读取，否则从远程读取实例绑定。</returns>
        public static T Load(PropertyBindIdentity identity)
        {
            return new DataProxy<T>().GetEntry(identity, true);
        }

    }

#if UnitTest
    [TestFixture(Description = "数据获取代理测试")]
    public class ProxyHelpTest
    {

        [Test(Description = "临时测试")]
        public void Test()
        {
            //SpecialCache[] specialConfig = new SpecialCache[]
            //{
            //    new SpecialCache() { CacheType = typeof(BestTone.DataSource.Model.Food.resultlist).FullName, CacheMinutes = 5.00d },
            //    new SpecialCache() { CacheType = this.GetType().FullName, CacheMinutes = 1.00d }
            //};

            //CacheConfig config = new CacheConfig() { CacheMinutes = 30, ImpTypeFullName = typeof(OrmCachedPolicy<>).FullName };
            //config.SpecialCaches = specialConfig;

            //config.GetXmlDoc().WriteIndentedContent(Console.Out);
            //Console.WriteLine();

            //CacheConfig set = CacheConfig.ConfigInstance;
            //set.GetXmlDoc().WriteIndentedContent(Console.Out);

            DataProxy<FoodResult> proxy = new DataProxy<FoodResult>();
            try
            {
                FoodResult result = proxy.GetEntry(new PropertyBindIdentity("id", "398555"), false);
                //Console.WriteLine(result.ToJSON());
                if (result != null)
                {
                    result.GetXmlDoc(true).WriteIndentedContent(Console.Out);
                }
            }
            catch (ApiException exp)
            {
                Console.WriteLine("获取数据错误：");
                Console.WriteLine(exp.Error.ToString());
            }

        }

        [Test]
        public void 自动建表()
        {
            Gwsoft.SharpOrm.OrmHelper.InitializeTables();
        }

    }

#endif

}
